﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using System.Threading;

namespace Neovolve.Extensibility.VisualStudio.ReflectorLink
{
    /// <remarks>
    /// This code was modelled of the RemoteController class in the Hawkeye project developed by Corneliu I. Tusnea. 
    /// See <a href="http://www.codeplex.com/hawkeye/SourceControl/FileView.aspx?itemId=139184&changeSetId=6535" target="_blank">here</a>.
    /// </remarks>
    internal static class ReflectorLauncher
    {
        /// <summary>
        /// Executes the specified actions.
        /// </summary>
        /// <param name="actions">The actions.</param>
        /// <returns></returns>
        public static Boolean Execute(LaunchActionCollection actions)
        {
            if (actions == null
                || actions.Count == 0)
            {
                // We successfully didn't do anything
                // This must return true otherwise the user will be asked to resolve the location of Reflector which is not the problem.
                return true;
            }

            foreach (LaunchAction action in actions)
            {
                switch (action.ActionType)
                {
                    case LaunchActionType.Default:

                        if (Show() == false)
                        {
                            return false;
                        }

                        break;

                    case LaunchActionType.Assembly:

                        if (ShowAssembly(action.AssemblyPath) == false)
                        {
                            return false;
                        }

                        break;

                    default:

                        throw new ArgumentOutOfRangeException("LaunchAction.ActionType");
                }
            }

            return true;
        }

        /// <summary>
        /// Shows this instance.
        /// </summary>
        /// <returns></returns>
        public static Boolean Show()
        {
            return EnsureIsRunning();
        }

        /// <summary>
        /// Shows the assemblies.
        /// </summary>
        /// <param name="references">The references.</param>
        /// <returns></returns>
        public static Boolean ShowAssemblies(List<String> references)
        {
            for (Int32 index = 0; index < references.Count; index++)
            {
                if (ShowAssembly(references[index]) == false)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Shows the assembly.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public static Boolean ShowAssembly(Type type)
        {
            return ShowAssembly(type.Module.FullyQualifiedName);
        }

        /// <summary>
        /// Shows the assembly.
        /// </summary>
        /// <param name="path">The path.</param>
        /// <returns></returns>
        public static Boolean ShowAssembly(String path)
        {
            // There is no need to send another message to Reflector as loading the assembly will select it
            // regardless of whether it is already loaded.
            return LoadAssembly(path);
        }

        public static Boolean ShowEvent(Type type, EventInfo eventInfo)
        {
            if (LoadAssembly(type) == false)
            {
                return false;
            }

            String name = type.FullName + "." + eventInfo.Name;

            return ReflectorController.SelectEventDeclaration("E:" + name);
        }

        public static Boolean ShowField(Type type, FieldInfo fieldInfo)
        {
            if (LoadAssembly(type) == false)
            {
                return false;
            }

            String name = type.FullName + "." + fieldInfo.Name;

            return ReflectorController.SelectFieldDeclaration("F:" + name);
        }

        public static Boolean ShowMethod(Type type, MethodInfo methodInfo)
        {
            if (LoadAssembly(type) == false)
            {
                return false;
            }

            StringBuilder builder = new StringBuilder();
            builder.Append(type.FullName + "." + methodInfo.Name + "(");

            bool hasParams = false;

            // we also have to add all the parameter info
            foreach (ParameterInfo param in methodInfo.GetParameters())
            {
                builder.Append(param.ParameterType.FullName);
                builder.Append(",");
                hasParams = true;
            }

            if (hasParams)
            {
                builder.Remove(builder.Length - 1, 1);
                builder.Append(")");
            }
            else
            {
                builder.Remove(builder.Length - 1, 1);
            }

            return ReflectorController.SelectMethodDeclaration("M:" + builder);
        }

        public static Boolean ShowNamespace(Type type)
        {
            if (LoadAssembly(type) == false)
            {
                return false;
            }

            String name = type.Namespace;

            return ReflectorController.SelectNamespaceDeclaration("N:" + name);
        }

        public static Boolean ShowProperty(Type type, PropertyInfo propertyInfo)
        {
            return ShowProperty(type, propertyInfo.Name);
        }

        public static Boolean ShowProperty(Type type, String propertyName)
        {
            if (LoadAssembly(type) == false)
            {
                return false;
            }

            String name = type.FullName + "." + propertyName;

            return ReflectorController.SelectPropertyDeclaration("P:" + name);
        }

        public static Boolean ShowType(Type type)
        {
            if (LoadAssembly(type) == false)
            {
                return false;
            }

            String name = type.FullName;

            return ReflectorController.SelectTypeDeclaration("T:" + name);
        }

        private static Boolean EnsureIsRunning()
        {
            // Attempt to find whether Reflector is running
            if (ReflectorController.IsRunning == false)
            {
                if (LaunchReflector() == false)
                {
                    return false;
                }
            }

            // Set Reflector as the foreground application with focus
            ReflectorController.SetFocus();

            return true;
        }

        /// <summary>
        /// Gets the is available.
        /// </summary>
        private static void GetIsAvailable()
        {
            const Int32 RetryLength = 50;

            while (true)
            {
                if (ReflectorController.IsSupported)
                {
                    break;
                }

                Thread.Sleep(RetryLength);
            }
        }

        /// <summary>
        /// Launches the reflector.
        /// </summary>
        /// <returns></returns>
        private static Boolean LaunchReflector()
        {
            String reflectorPath;

            if (ReflectorResolver.CanResolve)
            {
                reflectorPath = ReflectorResolver.GetReflectorPath();
            }
            else
            {
                return false;
            }

            // Launch the application
            Process reflectorProcess = Process.Start(reflectorPath);

            if (reflectorProcess == null)
            {
                return false;
            }

            const Int32 TimeoutLength = 5000;

            reflectorProcess.WaitForInputIdle(TimeoutLength);

            DelegateInvoker invoker = new DelegateInvoker();

            invoker.RunDelegate(GetIsAvailable, TimeoutLength);

            return true;
        }

        private static Boolean LoadAssembly(String path)
        {
            if (EnsureIsRunning() == false)
            {
                return false;
            }

            if (ReflectorController.IsSupported == false)
            {
                return false;
            }

            return ReflectorController.LoadAssembly(path);
        }

        private static Boolean LoadAssembly(Type type)
        {
            String fullName = type.Module.FullyQualifiedName;

            return LoadAssembly(fullName);
        }
    }
}